<?php
// +----------------------------
// | Created by PhpStorm
// +----------------------------
// | Author: YnnSon
// +----------------------------
namespace app\api\service;

use app\lib\exception\TokenException;
use think\Cache;
use think\Config;
use think\Exception;
use Yurun\Util\HttpRequest;

/**
 * 获取AccessToken
 * Class AccessToken
 * @package app\api\service
 */
class AccessToken extends Token
{
    private $access_token_url;

    public function __construct()
    {
        $config = Config::get('wechat');
        $this->access_token_url = sprintf($config['access_token_url'], $config['appid'], $config['appsecret']);
    }

    /**
     * @Desc 获取access_token, 先从缓存中取值, 不存在或过期则请求微信服务器
     * @return bool|mixed
     */
    public function get()
    {
        if ($access_token = $this->getAccessTokenFromCache()) {
            return $access_token;
        }
        $access_token = $this->getAccessTokenFromServer();
        $this->saveToCache($access_token);
        return $access_token;
    }

    /**
     * @Desc 从缓存中获取access_token
     * @return bool|mixed
     */
    private function getAccessTokenFromCache()
    {
        return Cache::get('access_token') ?: false;
    }

    /**
     * @Desc 向微信服务器请求获取access_token
     * @return bool|mixed
     */
    private function getAccessTokenFromServer()
    {
        $http = new HttpRequest();
        $response = $http->get($this->access_token_url)->json(true);
        if (array_key_exists('errcode', $response)) {
            throw new TokenException([
                'message'   => $response['errmsg'],
                'errorCode' => $response['errcode'],
            ]);
        }
        return $response['access_token'];
    }

    /**
     * @Desc 将access_token存入缓存
     *
     * @param string $access_token
     *
     * @return bool
     */
    private function saveToCache($access_token)
    {
        $result = Cache::set('access_token', $access_token, Config::get('setting.accessToken_expire_in'));
        if (!$result) {
            throw new Exception('缓存服务器错误');
        }
        return true;
    }
}